#include <hardwarecommunication/interrupts.h>
#include <hardwarecommunication/pci.h>
#include <common/types.h>   /*一般来说，同一个目录下的头文件用双引号，不同目录的用尖括号*/
#include <gdt.h>
#include <drivers/keyboard.h>
#include <drivers/mouse.h>
#include <drivers/driver.h>
#include <drivers/vga.h>
#include <gui/desktop.h>
#include <gui/window.h>
#include <multitasking.h>
#include <memorymanagement.h>

//#define GRAPHICSMODE

using namespace myos;
using namespace myos::common;
using namespace myos::drivers;
using namespace myos::hardwarecommunication;
using namespace myos::gui;

void printf(char* str)
{
	static uint16_t* VideoMemory=(uint16_t*)0xb8000;
	//设置游标
	static uint8_t x=0,y=0;

	for(int i=0; str[i] !='\0'; ++i){
		switch(str[i])
		{
			case '\n':
				y++;
				x=0;
				break;
			default:
				VideoMemory[80*y + x]=(VideoMemory[ 80 * y+x]& 0xFF00) | str[i];
				x++;
				break;
		}
		if (x >= 80){
			y++;
			x=0;
		}

		if (y >= 25)
		{
			for(y=0;y<25;y++)
				for(x=0;x< 80;x++)
					VideoMemory[80*y+x]=(VideoMemory[80*y+x]& 0xFF00) | ' ';
			x=0;
			y=0;
		}
	}

}

void printfHex(uint8_t key){
	char* foo=(char*)"00 ";
	char* hex=(char*)"0123456789ABCDEF";
	foo[0]=hex[(key>>4)&0x0F];
	foo[1]=hex[key & 0x0F];
	//把阻塞的中断打印出来
	printf(foo);
}

class PrintfKeyboadEventHandler:public KeyboardEventHandle{
public:
	void OnkeyDown(char c){
		char* foo=" ";
		foo[0]=c;
		printf(foo);
	}
};

class MouseToConsole : public MouseEventHandle{
	int8_t x,y;
public:
	MouseToConsole(){
		uint16_t* VideoMemeory=(uint16_t *)0xb8000;
		x=40;
		y=12;
		VideoMemeory[80*12+40]=((VideoMemeory[80*12+40] & 0xF000)>>4)
								|((VideoMemeory[80*12+40] & 0x0F00)<< 4)
								|(VideoMemeory[80*12+40] & 0x0F00);
	}

	void OnMOuseMove(int xoffset,int yoffset){
		static uint16_t* VideoMemeory=(uint16_t *)0xb8000;
		VideoMemeory[80*y+x]=((VideoMemeory[80*y+x] & 0xF000)>> 4)
							|((VideoMemeory[80*y+x] & 0x0F00)<< 4)
							|(VideoMemeory[80*y+x] & 0x0F00);
		x+=xoffset;
		//调整鼠标到下一行
		if(x<0) x=0;
		if(x>=80) x=79;
		y+=yoffset;
		if(y<0) y=0;
		if(y>=25) x=24;
		//鼠标镜像翻转
		VideoMemeory[80*y+x]=((VideoMemeory[80*y+x] & 0xF000)>>4)
							|((VideoMemeory[80*y+x] & 0x0F00)<< 4)
							|(VideoMemeory[80*y+x] & 0x0F00);
	}
};

void taskA(){
	while(true){
		printf("A");
	}
}

void taskB(){
	while(true){
		printf("B");
	}
}

void taskC(){
	while(true){
		printf("C");
	}
}

void taskD(){
	while(true){
		printf("D");
	}
}

typedef void (*constructor)();

extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors()
{
	for(constructor* i = &start_ctors;i!=&end_ctors;i++){
		(*i)();
	}
}


extern "C" void kernelMain(void* multiboot_structure,uint32_t /*multiboot_magic*/){
	printf("hello world\n");
	printf("I'm Sanfen\n");

	GlobalDescriptorTable gdt;
	uint32_t* memupper =(uint32_t*)((size_t)multiboot_structure+8);
	size_t heap=10*1024*1024;
	MemoryManager memoryManager(heap,(*memupper) *1024-heap-10*1024);
	printf(" heap: 0x");
	printfHex((heap>>24) &0xFF);
	printfHex((heap>>16) &0xFF);
	printfHex((heap>>8) &0xFF);
	printfHex(heap & 0xFF);

	void* allocated=memoryManager.malloc(1024);
	printf("\n allocated:0x");
	printfHex(((size_t) allocated>>24) & 0xFF);
	printfHex(((size_t) allocated>>16) & 0xFF);
	printfHex(((size_t) allocated>>8) & 0xFF);
	printfHex(((size_t) allocated) &0xFF);
	printf("\n");

	TaskManager taskManager;
//	Task task1(&gdt,taskA);
//	Task task2(&gdt,taskB);
//	taskManager.AddTask(&task1);
//	taskManager.AddTask(&task2);

	InterruptManager interrupts(0x20,&gdt,&taskManager);

	printf("Initalizing Hardware, Stage 1\n");

#ifdef GRAPHICSMODE
	Desktop desktop(320, 200, 0x00, 0x00, 0XA8);
#endif
	DriverManager drvManager;




#ifdef GRAPHICSMODE
		KeyboardDrive keyboard(&interrupts,&desktop);
#else
		PrintfKeyboadEventHandler kbhandler;
		KeyboardDrive keyboard(&interrupts,&kbhandler);
#endif

	drvManager.AddDriver(&keyboard);

#ifdef GRAPHICSMODE
	MouseDrive mouse(&interrupts,&desktop);
#else
	MouseToConsole mousehandler;
	MouseDrive mouse(&interrupts,&mousehandler);
#endif
	drvManager.AddDriver(&mouse);


	PeripheralComponentInterconnectController PCIController;
	PCIController.SelectDrivers(&drvManager,&interrupts);
	VideoGraphicsArray vga;

	printf("Initalizing Hardware, Stage 2\n");
	drvManager.ActiveAll();

	printf("Initalizing Hardware, Stage 3\n");

#ifdef GRAPHICSMODE
	vga.SetMode(320, 200, 8);
	Window win1(&desktop,10,10,20,20,0XA8,0x00,0x00);
	desktop.AddChild(&win1);
	Window win2(&desktop,40,15,30,30,0X00,0xA8,0x00);
	desktop.AddChild(&win2);
#endif

	interrupts.Activate();

	while(1)
	{
#ifdef GRAPHICSMODE
		desktop.Draw(&vga);
#endif
	}
}
